package net.aiion.weave.impl.conversions;

import java.util.Collection;

import net.aiion.weave.spi.bytecode.Bytecode;
import net.aiion.weave.spi.conversions.Converter;
import net.aiion.weave.spi.conversions.ConverterContext;
import net.aiion.weave.spi.conversions.ConverterException;
import net.aiion.weave.spi.conversions.ConverterService;
import net.aiion.weave.spi.injection.annotations.Inject;
import net.aiion.weave.spi.naming.Context;
import net.aiion.weave.spi.naming.annotations.Named;
import net.aiion.weave.spi.naming.annotations.NotScoped;

@NotScoped
@Named
public class ConverterServiceImpl implements ConverterService
{
    @Inject
    private Context context;

    public ConverterServiceImpl() {
    }

    @SuppressWarnings("unchecked")
    public <T1, T2> T1 convertValue(final T2 aSourceValue, final Class<T1> aTargetType) {
        if (aSourceValue == null || aTargetType.isInstance(aSourceValue)) {
            return (T1) aSourceValue;
        }

        final ConverterContext converterContext = new ConverterContextImpl(createConverters(), context, aSourceValue,
            aSourceValue.getClass(), aTargetType);
        final Object targetValue = converterContext.forwardConversion();
        if (aTargetType.isInstance(targetValue)) {
            return aTargetType.cast(targetValue);
        }

        throw new ConverterException(String.format("Cannot convert '%s' of type '%s' to type '%s'.", aSourceValue,
            aSourceValue.getClass(), aTargetType));
    }

    private Collection<? extends Converter> createConverters() {
        return context.lookupAll(Bytecode.findClassesByInterfaceClass(Converter.class));
    }
}
